package com.zoanun.common.interceptors;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class ParamListConstructorInterceptor extends HandlerInterceptorAdapter {

	Logger logger = LoggerFactory.getLogger(ParamListConstructorInterceptor.class);
	
	Map<String,Map<String,Method>> methodMap = new HashMap<String,Map<String,Method>>();
	
	@SuppressWarnings("unchecked")
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		// 检查是否需要组装list列表。
		// 参数必须有listClassName 和 keyColumn这两个
		// listClassName 决定组装什么类， keyColunm 决定组装的类的个数
		// request.getParameterValues(keyColumn).length
		String className = StringUtils.trimToEmpty(request.getParameter("listClassName"));
		String keyColumn = StringUtils.trimToEmpty(request.getParameter("keyColumn"));
		if (className.length() == 0 || keyColumn.length() == 0) {
			return true;
		}
		// list长度为0，不组装
		int size = request.getParameterValues(keyColumn).length;
		if (size == 0) {
			return true;
		}

		Class<?> clazz = null;
		try {
			// 检查是否存在这个类
			clazz = Class.forName(className);
			clazz.newInstance();
		} catch (Exception e) {
			throw new ClassNotFoundException("listClassName is " + className, e);
		}
		// 组装类并返回
		List<?> list = retrieveList(request.getParameterMap(), clazz, size);
		request.getSession().setAttribute("classList", list);
		return true;
	}
	
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private List retrieveList(Map<String, String[]> paramMap, Class clazz, int size) throws Exception {
		// 初始化返回列表
		List list = new ArrayList();
		for (int i = 0; i < size; i++) {
			list.add(clazz.newInstance());
		}
		for (String name : paramMap.keySet()) {
			String[] values = paramMap.get(name);
			if (values.length == size) {
				if(setPropertyForList(list, name, values) == false)
					continue;
			}
		}
		return list;
	}

	private boolean setPropertyForList(List<?> list,String name,String[] values) {
		if(list ==null || list.isEmpty()){
			return false;
		}
		for(int i=0;i<list.size();i++){
			Object o = list.get(i);
			String value = values[i];
			if(setProperty(o,name,value) == false)
				return false;
		}
		return true;
	}

	private boolean setProperty(Object o, String name, String value) {
		try {
			String propClass = name;
			int pos = name.indexOf('.');
			if(pos >=0){
				propClass = name.substring(0,pos);
				Object propObj = handlePropClass(o,propClass);
				setProperty(propObj, name.substring(pos+1), value);
			}

			// 得到setter方法
			String setterName = getSetterName(propClass);
			Method m = retrieveMethod(o,setterName);
			if(m == null) throw new NoSuchMethodException(setterName);
			Class<?>[] types = m.getParameterTypes();
			if (types.length != 1) {
				return false;
			} else {
				Class<?> type = types[0];
				// set 值
				if (type.isPrimitive()){
					m.invoke(o, value);
				} else if (type == String.class) {
					m.invoke(o, value);
				} else if (type == Integer.class) {
					m.invoke(o, Integer.parseInt(value));
				} else if (type == Double.class) {
					m.invoke(o, Double.parseDouble(value));
				} else if (type == Short.class) {
					m.invoke(o, Short.parseShort(value));
				} else if (type == Double.class) {
					m.invoke(o, Double.parseDouble(value));
				} else if (type == Byte.class) {
					m.invoke(o, Byte.parseByte(value));
				} else if (type == Character.class){
					if(value.length()==1){
						m.invoke(o, value.charAt(0));
					}else{
						throw new IllegalArgumentException("Character type value length must be one!");
					}
				} else if (type == Long.class){
					m.invoke(o, Long.parseLong(value));
				} else if (type == Float.class){
					m.invoke(o, Float.parseFloat(value));
				} else if (type == Boolean.class) {
					if(value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")){
						m.invoke(o, Boolean.parseBoolean(value));
					} else if(value.equalsIgnoreCase("Y")){
						m.invoke(o, true);
					} else if(value.equalsIgnoreCase("N")){
						m.invoke(o, false);
					} else {
						try{
							if(Integer.parseInt(value) > 0) m.invoke(o, true);
							else m.invoke(o, false);
						}catch(NumberFormatException e){
							throw new IllegalArgumentException("Boolean type value must be true or false or Y or N or integer");
						}
					}
				} else if (type == java.util.Date.class){
					java.util.Date d = null;
					SimpleDateFormat sdf = null;
					if(value.length()==8){
						sdf = new SimpleDateFormat("yyyyMMdd");
					}else if(value.length() == 10){
						 sdf = new SimpleDateFormat("yyyy-MM-dd");
					} else if(value.length() == 16){
						sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
					} else if(value.length() == 19){
						sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
					}
					d = sdf.parse(value);
					m.invoke(o, d);
				} else if (type == java.sql.Date.class){
					java.sql.Date d = null;
					SimpleDateFormat sdf = null;
					if(value.length()==8){
						sdf = new SimpleDateFormat("yyyyMMdd");
					}else if(value.length() == 10){
						 sdf = new SimpleDateFormat("yyyy-MM-dd");
					} else if(value.length() == 16){
						sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
					} else if(value.length() == 19){
						sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
					}
					d = new java.sql.Date(sdf.parse(value).getTime());
					m.invoke(o, d);
				} else {
					logger.warn("Unsupport type " + type.getName());
				}
			}
		} catch(NoSuchMethodException e){
			return false;
		} catch (Exception e){
			logger.warn(e.getMessage(),e);
			return false;
		}
		return true;
	}

	private Method retrieveMethod(Object o, String setterName) {
		if(methodMap.containsKey(o.getClass().getName())){
			return methodMap.get(o.getClass().getName()).get(setterName);
		}else{
			cacheMethodMap(o);
			return retrieveMethod(o,setterName);
		}
	}

	private void cacheMethodMap(Object o) {
		Map<String,Method> map = new HashMap<String,Method>();
		methodMap.put(o.getClass().getName(), map);
		for(Method m:o.getClass().getDeclaredMethods()){
			map.put(m.getName(), m);
		}
	}


	private Object handlePropClass(Object o, String propClass) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Method 	m = retrieveMethod(o,getGetterName(propClass));
		Object retO = m.invoke(o);
		if(retO == null){
			Object tmpO =  m.getReturnType().newInstance();
			m = retrieveMethod(o,getSetterName(propClass));
			m.invoke(o, tmpO);
			retO = tmpO;
		}
		return retO;
	}

	private String getSetterName(String name) {
		if (StringUtils.isEmpty(name)) {
			throw new IllegalArgumentException("parameter name should not be empty");
		}
		return "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
	}
	
	private String getGetterName(String name) {
		if (StringUtils.isEmpty(name)) {
			throw new IllegalArgumentException("parameter name should not be empty");
		}
		return "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
	}
	
	public static void main(String[] args) throws ClassNotFoundException{
	}


	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		super.postHandle(request, response, handler, modelAndView);
		request.getSession().removeAttribute("classList");
	}
}

